//
//  at24cxx.c
//  electronic_clock
//
//  Created by fukai on 2018/10.
//  Copyright © 2018年 赛博智能车实验室. All rights reserved.
//

#include "at24cxx.h"
#include "tick_timer.h"


/********IIC通信部分********/

//AT24CXX初始化IIC接口
void At24cxxIICInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	IIC_RCC, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = IIC_SCL_Pin|IIC_SDA_Pin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(IIC_GPIO, &GPIO_InitStructure);
 
	SCL_H();
	SDA_H();
}

//IIC起始信号
//SCL高电平期间，SDA出现一个下降沿表示起始信号
void IicStart(void)
{
	SDA_DIR_OUT();	//主机数据线输出
	SDA_H();	//数据线先保持为高，起始信号要改口的下降沿
	SCL_H();	//时钟线保持为高
	DelayUs(10);
	SDA_L();	//数据线拉低出现下降沿
	DelayUs(10);	//延时保证一个可靠的下降沿
	SCL_L();	//拉低时钟线，保证接下来数据可以改变
}

//IIC停止信号
//SCL高电平期间，SDA出现一个上升沿表示停止信号
void IicStop(void)
{
	SDA_DIR_OUT();	//主机数据线输出
	SCL_H();
	SDA_L();
	DelayUs(10);
	SDA_H();	//数据线产生上升沿
	DelayUs(10);	//延时保证可靠的上升沿
}

//主机等待应答信号的到来
//返回值：1，接收应答失败
//		  0，接收应答成功
unsigned char IicWaitAsk(void)
{
	unsigned char err_time = 0;
	SDA_DIR_IN();	//将SDA设置成输入
	SDA_H();
	DelayUs(10);
	SCL_H();	//时钟线拉高
	DelayUs(10);
	while(SDA_READ())	//等待接收器把时钟线拉低，如果接收器拉低了时钟线，表示响应了
	{
		err_time++;
		if(err_time > 250)
		{
			IicStop();
			return 1;
		}
	}
	SCL_L();	//拉低时钟线，保证接下来数据可以改变
	return 0;
}

//产生AcK应答
//SDA低电平期间，SCL出现一个上升沿即为应答
void IicAck(void)
{
	SDA_DIR_OUT();	//主机数据线输出
	SCL_L();
	SDA_L();
	DelayUs(10);
	SCL_H();	//时钟线拉高
	DelayUs(10);	//延时保证可靠的上升沿
	SCL_L();	//应答完成后拉低时钟线，保证接下来数据线允许改变
}

//不产生AcK应答
//SDA高电平期间，SCL出现一个上升沿即为非应答
void IicNoAck(void)
{
	SDA_DIR_OUT();	//主机数据线输出
	SCL_L();
	SDA_H();
	DelayUs(10);
	SCL_H();	//时钟线拉高
	DelayUs(10);	//延时保证可靠的上升沿
	SCL_L();	//应答完成后拉低时钟线，保证接下来数据线允许改变
}

//IIC发送一个字节
//txd:发送的数据
void IicSendByte(unsigned char txd)
{
	unsigned char count = 0;	//定义一个计数变量
	SDA_DIR_OUT();	//主机数据线输出
	SCL_L();	//拉低时钟线，保证接下来的数据允许改变
	
	for(count = 0; count < 8; ++count)
	{
		if(txd & 0x80)	//按位与
		{
			SDA_H();
		}
		else
		{
			SDA_L();
		}
		txd <<= 1;	//保证将要发送的位一直是最高位
		DelayUs(10);
		SCL_H();	//时钟线拉高,此时数据线不得改变，用于对方读取数据
		DelayUs(10);
		SCL_L();	//拉低时钟线，保证接下来的数据允许改变
		DelayUs(10);
	}
}

//IIC读取一个字节
//ack=1时，发送ACK，ack=0，发送nACK 
unsigned char IicReadByte(unsigned char ack)
{
	unsigned char count = 0;	//定义计数变量
	unsigned char receive = 0;	//定义接收变量
	SDA_DIR_IN();	//将SDA设置成输入
	
	for(count = 0; count < 8; ++count)
	{
		SCL_L();	//拉低时钟线，保证接下来的数据允许改变
		DelayUs(10);
		SCL_H();
		receive <<= 1;
		if(SDA_READ())
		{
			receive |= 0x01;	//高电平的话接收变量自加，低电平不变化只左移，即保证了该位为0
		}
		DelayUs(10);
	}
	
	if(!ack)
	{
		IicNoAck();	//发送NAck
	}
	else
	{
		IicAck();	//发送Ack
	}
	
	return receive;
}


/********AT24CXX部分********/

//写数据
//data_addr：数据要写入内存地址
//data:数据
void At24cxxWriteData(unsigned char data_addr, unsigned int data)
{
	IicStart();
	IicSendByte(AT24C02_WRITE_ADDR);	//发送写器件地址
	IicSendByte(data_addr);		//发送要写入内存地址
	IicSendByte(data);	//发送数据
	IicStop();
}

//读数据
//data_addr:数据地址
unsigned int At24cxxReadData(unsigned char data_addr)
{
	unsigned int data = 0;
	IicStart();
	IicSendByte(AT24C02_WRITE_ADDR);	//发送写器件地址
	IicSendByte(data_addr);		//发送要读取的地址
	
	IicStart();
	IicSendByte(AT24C02_READ_ADDR);//发送读器件地址
	data = IicReadByte(1);
	data <<=8;
	data |= IicReadByte(0);
	IicStop();
	return data;
}






